<template>
	<view class="virtual-wrap">

    <!-- flex=true 头部渲染 -->
    <view class="virtual-header"
			<!-- #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO || MP-BAIDU  -->
			v-if="flex&&(zScopedSlots.header||zSlots.header)"
			<!-- #endif -->
			<!-- #ifndef MP-WEIXIN || MP-QQ || MP-TOUTIAO || MP-BAIDU -->
			v-if="flex&&($scopedSlots.header||$slots.header)"
			<!-- #endif -->
      >
      <slot name="header" ></slot>
    </view>

    <scroll-view scroll-y="true"  class="virtual-list-scroller" @scroll="scroll" >

      <!-- 高度占位 -->
      <view class="list-scroller-holder"  :style="{'minHeight': totalHeight}" ></view>

      <!-- 列表盒子 -->
      <view class="list-scroller-wrapper" :style="{transform: `translateY(${offset}px)` }" >
        <!-- flex=false 头部渲染 -->
        <view class="virtual-header" 
          <!-- #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO || MP-BAIDU  -->
          v-if="!flex&&(zScopedSlots.header||zSlots.header)"
          <!-- #endif -->
          <!-- #ifndef MP-WEIXIN || MP-QQ || MP-TOUTIAO || MP-BAIDU -->
          v-if="!flex&&($scopedSlots.header||$slots.header)"
          <!-- #endif -->>
          <slot name="header" ></slot>
        </view>

        <!-- 渲染列表 -->
        <view class="list-scroller-item" v-for="(item, index) in viewList" :key="item[itemKey] || index">
          <slot :data="item" />
        </view>

        
        <view class="virtual-bottom" 
          <!-- #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO || MP-BAIDU  -->
          v-if="zScopedSlots.bottom||zSlots.bottom"
          <!-- #endif -->
          <!-- #ifndef MP-WEIXIN || MP-QQ || MP-TOUTIAO || MP-BAIDU -->
          v-if="$scopedSlots.bottom||$slots.bottom"
          <!-- #endif -->>
          <slot name="bottom" ></slot>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

<script>
/* 
使用示例：
<mo-virtual-scroller
:list="list">
  <template v-slot="{ data }">
    <view> {{ data.value }} </view>
  </template>
</mo-virtual-scroller>
 */
import { throttle, getNodeClientRect } from './js/utils'
export default {
    name: "mo-virtual-scroller",
    props: {
        list: {
            type: Array,
            default: () => [],
            require: true
        },
        itemKey: {
            type: String,
            default: 'id',
        },
        flex: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            itemHeight: 20, //单个列表的高度
            viewHeight: 0, // 可视区域高度
            scrollTop: 0,
            headerHeight: 0,
            start: 0,
            end: this.list.length ? 1 : 0
        }
    },
    computed: {
        zScopedSlots() {
            return this.$scopedSlots;
        },
        zSlots() {
            return this.$slots;
        },
        totalHeight() {
            let height = this.itemHeight * this.list.length || this.viewHeight
            height = this.flex ? height : height + this.headerHeight
            return height + 'px';
        },
        viewList(){ // 当前渲染的数据
            return this.list.slice(this.start, this.end)
        },
        offset(){ // 当前渲染数据区域的Y轴偏移量
            return this.start * this.itemHeight
        }
    },
    watch: {
        list(){
            this.$nextTick(_=>{
                this.scroll()
            })
        },
        itemHeight(){
            this.scroll()
        },
    },
    mounted() {
        this.scroll()
        uni.onWindowResize(this.scroll)
    },
    beforeDestroy() {
        uni.offWindowResize(() => { })
    },
    methods: {
        scroll(e) {
            this.$emit('scroll',e)
            this.scrollTop = (e && e.detail && e.detail.scrollTop) || this.scrollTop
            this.update(this)
        },
        update: throttle(function(arg){
            const _this = arg[0]
            _this.setHeight()
            // if(!_this.itemHeight) return
            const one = Math.floor(_this.scrollTop / _this.itemHeight);
            const num = Math.floor(_this.viewHeight / _this.itemHeight);
            _this.start = one - num > 0 ? one - num : 0;
            _this.end = one + (2 * num) < _this.list.length ? one + (2 * num) : _this.list.length;
        }, 12),
        setHeight(){
            getNodeClientRect('.list-scroller-item', this).then((res) => {
                res && (this.itemHeight = res[0][0].height)
            });
            getNodeClientRect('.virtual-list-scroller', this).then((res) => {
                res && (this.viewHeight = res[0][0].height)
            });
            getNodeClientRect('.virtual-header', this).then((res) => {
                res && (this.headerHeight = res[0][0].height)
            });
        }
    }
};
</script>

<style lang="scss" scoped>
.virtual-wrap{
  display: flex;
  height: 100%;
  width: 100%;
  min-height: 200rpx;
  flex-direction: column;
}
.virtual-list-scroller{
  flex: 1;
  height: 100%;
  position: relative;
  overflow: hidden;
}
.list-scroller-holder{
  width: 100%;
}
.list-scroller-wrapper{
  width: 100%;
  position: absolute;
  top: 0;
}

</style>